package QC.MessageBox.handler;

import QC.MessageBox.beans.WXAccessToken;
import QC.MessageBox.beans.WXPublic;
import QC.MessageBox.beans.WXUser;
import QC.MessageBox.beans.rcform.RCSingleForm;
import QC.MessageBox.common.Constants;
import QC.MessageBox.common.PeriodicTime;
import QC.MessageBox.enums.ChannelType;
import QC.MessageBox.service.*;
import io.vertx.core.CompositeFuture;
import io.vertx.core.Future;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * Created by Andaren on 2017/5/9.
 * Used for: 处理各类请求的基类
 */
public abstract class HandlerBase {

    private static final Logger LOGGER = LoggerFactory.getLogger(HandlerBase.class);

    protected HttpService httpService;
    protected MySQLService sqlService;
    protected RedisService redisService;
//    protected MongoService mongoService;
    protected DataAccess dataAccess;
    protected WebClientService webClientService;

    public HandlerBase() {

    }

    /**
     * 更新微信接口调用access_token
     */
    public void updateAccessTokenByAll() {
        // 遍历所有公众号信息
        sqlService.getAllWxPublic().setHandler(as -> {
            if (as.succeeded()) {
                List<WXPublic> publicList = as.result();
                if (null != publicList && publicList.size() > 0) {
                    /*publicList.stream().forEach(pub -> {
                        // 获取access_token并缓存
                        updateAcessToken(pub);
                    });*/
                    for (WXPublic pub:publicList) {
                        // 获取access_token并缓存
                        updateAcessToken(pub);
                    }
                }
            } else {
                LOGGER.error("查询所有公众号信息异常：" + as.cause().getMessage());
                as.cause().printStackTrace();
            }
        });
    }

    /**
     * 更新单一公众号信息
     *      微信才去更新token，微博不用更新token
     * @param pub
     */
    public Future<String> updateAcessToken(WXPublic pub) {
        Future<String> updateFut = Future.future();
        if (ChannelType.WX.val() == pub.getChannelType()) {
            webClientService
                    .getAccessTokenByTime(Constants.GRANT_TYPE, pub.getAppId(), pub.getSecret())
                    .setHandler(accessResult -> {
                        if (accessResult.succeeded()) {
                            String accessToken = accessResult.result();
                            // test-------------------------------------------------------------
                            saveAccessTokenRequestByTest(accessToken);
                            //------------------------------------------------------------------
                            pub.setAccessToken(accessToken)
                                    .setUpdateTime(PeriodicTime.getCurrentTimeByyyyMMddHHmmss());

                            // save wxpublic info to redis
                            redisService.savePublicToRedis(pub.setAccessToken(accessToken)).setHandler(Future::succeededFuture);
                            // save public info to mysql
                            sqlService.updateAccessToken(pub.getDeveloperId(), accessToken);
                            updateFut.complete(accessToken);
                        } else {
                            LOGGER.error("获取access_token异常：" + accessResult.cause().getMessage());
                            updateFut.fail(accessResult.cause());
                        }
                    });
        } else {
            updateFut.complete(pub.getAccessToken());
        }
        return updateFut;
    }

    /**
     * 单独获取access_toeken
     * @param developerId
     * @return
     */
    public Future<String> getAccessTokenIfExpired(String developerId) {
        Future<String> tokenFut = Future.future();
        Future<WXPublic> pubFut = Future.future();
        dataAccess.getWXPublicInfoByDeveloperId(developerId).setHandler(pubFut.completer());
        pubFut.setHandler(pubAr -> {
            if (pubAr.failed()) {
                tokenFut.fail(pubAr.cause());
            } else {
                WXPublic pub = pubAr.result();
                String underCheckToken = pubAr.result().getAccessToken();
                webClientService.getWXUserInfoFromWX("", underCheckToken).setHandler(checkAr -> {
                    if (checkAr.failed()) {

                        String errorCode = checkAr.cause().getMessage();
                        if ("40001".equals(errorCode) || "42001".equals(errorCode)) {
                            webClientService.getAccessTokenByTime(Constants.GRANT_TYPE, pub.getAppId(), pub.getSecret())
                                    .compose(toeknAr -> {
                                        // test-------------------------------------------------------------
                                        saveAccessTokenRequestByTest(toeknAr);
                                        //------------------------------------------------------------------
                                        pub.setAccessToken(toeknAr)
                                                .setUpdateTime(PeriodicTime.getCurrentTimeByyyyMMddHHmmss());
                                        // 更新token
                                        // save wxpublic info to redis
                                        redisService.savePublicToRedis(pub).setHandler(Future::succeededFuture);
                                        // save public info to mysql
                                        sqlService.updateAccessToken(pub.getDeveloperId(), toeknAr);
                                        tokenFut.complete(toeknAr);
                                    }, tokenFut);
                        } else if ("40003".equals(errorCode)) {
                            tokenFut.complete(underCheckToken);
                        }
                    } else {
                        // can`t be true
                    }
                });
            }
        });
        return tokenFut;
    }

    /**
     * 一键拉取历史粉丝处理（成为预备粉丝）
     *      微博拉粉
     * @param
     */
    public Future pullFansFromWB(WXPublic pub) {
        Objects.requireNonNull(pub);

        Future fansListFut = Future.future();
        // 获取公众号下粉丝列表
        Future<List<String>> uidListFuture = Future.future();
        List<String> uidList = new ArrayList<>();
        webClientService.getUIdListByLoopFromWB(pub.getAccessToken(), null, uidListFuture, uidList);

        // 获取公众号下所有已加入的粉丝
        Future<List<String>> addedFansListFut = sqlService.getAllWxUserByDeveloperId(pub.getDeveloperId());

        // 历史粉丝变身===>预备粉丝
        CompositeFuture.all(uidListFuture, addedFansListFut)
                .compose(compositeFuture -> {
                    List<String> allFansList = compositeFuture.resultAt(0);
                    List<String> addedFansList = compositeFuture.resultAt(1);
                    // 没有加入的历史粉丝
                    List<String> unAddedFansList = allFansList.stream().filter(
                            openId -> !addedFansList.stream()
                                    .anyMatch(addedOpenId -> addedOpenId.equals(openId))
                    ).collect(Collectors.toList());

                    List<Future> saveFutList = new ArrayList<>();
                    unAddedFansList.stream()
                            .forEach(openId -> {
                                Future<WXUser> saveNewUserFut = Future.future();
                                // 查询用户信息
                                webClientService.getWBUserByUID(openId, pub.getAccessToken())
                                        .compose(wxUser -> {
                                            if (wxUser != null) {
                                                // 生成wx_user
                                                dataAccess.insertWxUser(wxUser)
                                                        .setHandler(saveNewUserFut.completer());
                                            } else {
                                                saveNewUserFut.complete();
                                            }
                                        }, saveNewUserFut);
                                saveFutList.add(saveNewUserFut);
                            });
                    // TODO 优化：不一定要等到所有的用户添加完毕，如果中间添加失败不应该影响后面的用户的添加
                    CompositeFuture.all(saveFutList)
                            .setHandler(allSaveAr -> {
                                if (allSaveAr.failed()) {
                                    fansListFut.fail(allSaveAr.cause());
                                } else {
                                    System.out.println("pull fans success!");
                                    fansListFut.complete();
                                }
                            });
                }, fansListFut);
        return fansListFut;
    }

    /**
     * 一键拉取历史粉丝处理（成为预备粉丝）
     * @param
     */
    public Future generateHistoryFansByDeveloperId(String developerId) {
        Objects.requireNonNull(developerId);

        Future fansListFut = Future.future();
        dataAccess.getWXPublicInfoByDeveloperId(developerId)
                .compose(wxPublic -> {
                    // 获取公众号下粉丝列表
                    Future<List<String>> openIdListFuture = Future.future();
                    List<String> openInList = new ArrayList<>();
                    webClientService.getOpenIdListByLoop(wxPublic.getAccessToken(), null, openIdListFuture, openInList);

                    // 获取公众号下所有已加入的粉丝
                    Future<List<String>> addedFansListFut = sqlService.getAllWxUserByDeveloperId(developerId);

                    // 历史粉丝变身===>预备粉丝
                    CompositeFuture.all(openIdListFuture, addedFansListFut)
                            .compose(compositeFuture -> {
                                List<String> allFansList = compositeFuture.resultAt(0);
                                List<String> addedFansList = compositeFuture.resultAt(1);
                                // 没有加入的历史粉丝
                                List<String> unAddedFansList = allFansList.stream().filter(openId ->
                                        !addedFansList.stream()
                                                .anyMatch(addedOpenId -> addedOpenId.equals(openId))
                                ).collect(Collectors.toList());

                                List<Future> saveFutList = new ArrayList<>();
                                unAddedFansList.stream()
                                        .forEach(openId -> {
                                            Future<WXUser> saveNewUserFut = Future.future();
                                            // 查询用户信息
                                            webClientService.getWXUserInfoFromWX(openId, wxPublic.getAccessToken())
                                                    .compose(wxUser -> {
                                                        // 生成wx_user
                                                        dataAccess.insertWxUser(wxUser)
                                                                .setHandler(saveNewUserFut.completer());
                                                    }, saveNewUserFut);
                                            saveFutList.add(saveNewUserFut);
                                        });
                                // TODO 优化：不一定要等到所有的用户添加完毕，如果中间添加失败不应该影响后面的用户的添加
                                CompositeFuture.all(saveFutList)
                                        .setHandler(allSaveAr -> {
                                            if (allSaveAr.failed()) {
                                                fansListFut.fail(allSaveAr.cause());
                                            } else {
                                                fansListFut.complete();
                                            }
                                        });
                            }, fansListFut);
                }, fansListFut);
        return fansListFut;
    }

    /**
     * 记录获取的token
     *     仅仅测试使用
     * @param token
     */
    public void saveAccessTokenRequestByTest(String token) {
        WXAccessToken wxAccessToken = new WXAccessToken();
        wxAccessToken.setToken(token)
                .setUpdateTime(PeriodicTime.getCurrentTimeByyyyMMddHHmmss());
        sqlService.save(wxAccessToken);
    }

    /**
     * 发送消息至融云平台
     * @param singleForm
     * @param fut
     */
    public void sendSingMsgToRC(RCSingleForm singleForm, Future<Void> fut) {
        // 发送消息至融云平台
        httpService.postSingleMsg(singleForm).setHandler(postAs -> {
            if (postAs.succeeded()) {
                fut.complete();
            } else {
                LOGGER.info("融云接口发送消息失败：" + postAs.cause().getMessage());
                fut.fail(postAs.cause());
            }
        });
    }

    /**
     * 通用future结束处理
     * @param fut
     * @param method
     * @param LOGGER
     */
    public void commonEndFuture(Future<?> fut, String method, Logger LOGGER) {
        fut.setHandler(ar -> {
            if (ar.failed()) {
                LOGGER.info("[{}失败]error={}", method, ar.cause().getMessage());
            } else {
                LOGGER.info("[{}成功]", method);
            }
        });
    }
}
